#include "stdafx.h"
#include "TextureShaderClass.h"


CTextureShaderClass::CTextureShaderClass(void) : m_pVertexShader(nullptr), m_pPixelShader(nullptr), m_pLayout(nullptr), m_pMatrixBuffer(nullptr), m_pSampleState(nullptr)
{
}


CTextureShaderClass::~CTextureShaderClass(void)
{
}

bool CTextureShaderClass::Initialize(ID3D11Device* pDevice, HWND hWnd)
{
	bool bResult = false;

	bResult = InitializeShader(pDevice, hWnd, L"Texture.vs", L"Texture.ps");
	if(bResult == false)
	{
		DEBUG_OUTPUT("Failed to initialize texture shader");
		return false;
	}

	return true;
}

void CTextureShaderClass::ShutDown()
{
	ShutDownShader();
}

bool CTextureShaderClass::Render(ID3D11DeviceContext* pDeviceContext, int nIndexCount, D3DXMATRIX d3dWorld, D3DXMATRIX d3dView, D3DXMATRIX d3dProj, ID3D11ShaderResourceView* pTexture)
{
	bool bResult = false;

	bResult = SetShaderParameters(pDeviceContext, d3dWorld, d3dView, d3dProj, pTexture);
	if(bResult == false)
	{
		DEBUG_OUTPUT("Failed to set texture shader parameters");
		return false;
	}

	RenderShader(pDeviceContext, nIndexCount);

	return true;
}

bool CTextureShaderClass::InitializeShader(ID3D11Device* pDevice, HWND hWnd, WCHAR* pwszVSTextureFileName, WCHAR* pwszPSTextureFileName)
{
	HRESULT hResult;
	ID3D10Blob* pErrorMessage = nullptr;
	ID3D10Blob* pVertexShaderBuffer = nullptr;
	ID3D10Blob* pPixelShaderBuffer = nullptr;
	D3D11_INPUT_ELEMENT_DESC stPolygonLayout[2];
	unsigned int unNumElements;
	D3D11_BUFFER_DESC stMatrixBufferDesc;
	D3D11_SAMPLER_DESC stSamplerDesc;

	hResult = D3DX11CompileFromFile(pwszVSTextureFileName, NULL, NULL, "TextureVertexShader", "vs_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &pVertexShaderBuffer, &pErrorMessage, NULL);
	if(FAILED(hResult))
	{
		if(pErrorMessage != nullptr)
		{
			OutputShaderErrorMessage(pErrorMessage, hWnd, pwszVSTextureFileName);
		}
		else
		{
			DEBUG_OUTPUT("Vertex texture shader not found");
		}

		return false;
	}

	hResult = D3DX11CompileFromFile(pwszPSTextureFileName, NULL, NULL, "TexturePixelShader", "ps_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &pPixelShaderBuffer, &pErrorMessage, NULL);
	if(FAILED(hResult))
	{
		if(pErrorMessage != nullptr)
		{
			OutputShaderErrorMessage(pErrorMessage, hWnd, pwszPSTextureFileName);
		}
		else
		{
			DEBUG_OUTPUT("Pixel texture shader not found");
		}

		return false;
	}

	hResult = pDevice->CreateVertexShader(pVertexShaderBuffer->GetBufferPointer(), pVertexShaderBuffer->GetBufferSize(), NULL, &m_pVertexShader);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create texture vertex shader");
		return false;
	}

	hResult = pDevice->CreatePixelShader(pPixelShaderBuffer->GetBufferPointer(), pPixelShaderBuffer->GetBufferSize(), NULL, &m_pPixelShader);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create texture pixel shader");
		return false;
	}

	stPolygonLayout[0].SemanticName = "POSITION";
	stPolygonLayout[0].SemanticIndex = 0;
	stPolygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	stPolygonLayout[0].InputSlot = 0;
	stPolygonLayout[0].AlignedByteOffset = 0;
	stPolygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	stPolygonLayout[0].InstanceDataStepRate = 0;

	stPolygonLayout[1].SemanticName = "TEXCOORD";
	stPolygonLayout[1].SemanticIndex = 0;
	stPolygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
	stPolygonLayout[1].InputSlot = 0;
	stPolygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	stPolygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	stPolygonLayout[1].InstanceDataStepRate = 0;

	unNumElements = sizeof(stPolygonLayout) / sizeof(stPolygonLayout[0]);

	hResult = pDevice->CreateInputLayout(stPolygonLayout, unNumElements, pVertexShaderBuffer->GetBufferPointer(), pVertexShaderBuffer->GetBufferSize(), &m_pLayout);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create texture input layout");
		return false;
	}

	pVertexShaderBuffer->Release();
	pVertexShaderBuffer = nullptr;

	pPixelShaderBuffer->Release();
	pVertexShaderBuffer = nullptr;

	stMatrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	stMatrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
	stMatrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	stMatrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	stMatrixBufferDesc.MiscFlags = 0;
	stMatrixBufferDesc.StructureByteStride = 0;

	hResult = pDevice->CreateBuffer(&stMatrixBufferDesc, NULL, &m_pMatrixBuffer);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create texture buffer");
		return false;
	}

	stSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	stSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	stSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	stSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	stSamplerDesc.MipLODBias = 0.0f;
	stSamplerDesc.MaxAnisotropy = 1;
	stSamplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	stSamplerDesc.BorderColor[0] = 0;
	stSamplerDesc.BorderColor[1] = 0;
	stSamplerDesc.BorderColor[2] = 0;
	stSamplerDesc.BorderColor[3] = 0;
	stSamplerDesc.MinLOD = 0;
	stSamplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

	hResult = pDevice->CreateSamplerState(&stSamplerDesc, &m_pSampleState);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to create texture smapler state");
		return false;
	}

	return true;
}

void CTextureShaderClass::ShutDownShader()
{
	if(m_pSampleState)
	{
		m_pSampleState->Release();
		m_pSampleState = 0;
	}

	if(m_pMatrixBuffer)
	{
		m_pMatrixBuffer->Release();
		m_pMatrixBuffer = 0;
	}

	if(m_pLayout)
	{
		m_pLayout->Release();
		m_pLayout = 0;
	}

	if(m_pPixelShader)
	{
		m_pPixelShader->Release();
		m_pPixelShader = 0;
	}

	if(m_pVertexShader)
	{
		m_pVertexShader->Release();
		m_pVertexShader = 0;
	}
}

void CTextureShaderClass::OutputShaderErrorMessage(ID3D10Blob* pErrorMessage, HWND hWnd, WCHAR* pwscShaderFileName)
{
	char* cpCompileErrors;
	unsigned long ulBufferSize;
	ofstream fout;

	cpCompileErrors = (char*)(pErrorMessage->GetBufferPointer());

	ulBufferSize = pErrorMessage->GetBufferSize();

	fout.open("shader-error.txt");

	for(unsigned long i = 0; i < ulBufferSize; ++i)
	{
		fout << cpCompileErrors[i];
	}

	fout.close();

	pErrorMessage->Release();
	pErrorMessage = 0;

	DEBUG_OUTPUT("Error compiling shader. Check shader-error.txt for message.");
}

bool CTextureShaderClass::SetShaderParameters(ID3D11DeviceContext* pDeviceContext, D3DXMATRIX d3dWorld, D3DXMATRIX d3dView, D3DXMATRIX d3dProj, ID3D11ShaderResourceView* pTexture)
{
	HRESULT hResult;
	D3D11_MAPPED_SUBRESOURCE stMappedResource;
	MatrixBufferType* pDataPtr;
	unsigned int unBufferNumber;
	
	D3DXMatrixTranspose(&d3dWorld, &d3dWorld);
	D3DXMatrixTranspose(&d3dView, &d3dView);
	D3DXMatrixTranspose(&d3dProj, &d3dProj);

	hResult = pDeviceContext->Map(m_pMatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &stMappedResource);
	if(FAILED(hResult))
	{
		DEBUG_OUTPUT("Failed to map the texture device context");
		return false;
	}

	pDataPtr = (MatrixBufferType*)stMappedResource.pData;

	pDataPtr->d3dWorldMatrix = d3dWorld;
	pDataPtr->d3dViewMatrix = d3dView;
	pDataPtr->d3dProjectionMatrix = d3dProj;

	pDeviceContext->Unmap(m_pMatrixBuffer, 0);

	unBufferNumber = 0;

	pDeviceContext->VSSetConstantBuffers(unBufferNumber, 1, &m_pMatrixBuffer);

	pDeviceContext->PSSetShaderResources(0, 1, &pTexture);

	return true;
}

void CTextureShaderClass::RenderShader(ID3D11DeviceContext* pDeviceContext, int nIndexCount)
{
	pDeviceContext->IASetInputLayout(m_pLayout);
	pDeviceContext->VSSetShader(m_pVertexShader, NULL, 0);
	pDeviceContext->PSSetShader(m_pPixelShader, NULL, 0);
	pDeviceContext->PSSetSamplers(0, 1, &m_pSampleState);
	pDeviceContext->DrawIndexed(nIndexCount, 0, 0);
}